package xin.mashibo;

import com.sansec.hsm.sjf.SJFAPI;
import com.sansec.hsm.sjf.SJFCrypto;
import com.sansec.impl.device.bean.GlobalData;
import com.sansec.impl.device.util.PrintUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Scanner;

/**
 * 加密服务类
 *
 * @author MaShibo
 * 日期：2018/3/22
 */
public class EncryptService {

	private static final Logger LOGGER = LoggerFactory.getLogger(EncryptService.class);

	///////////////////////////////////////////////////////////////////////////
	// 常量值
	///////////////////////////////////////////////////////////////////////////

	/**
	 * 加密配置文件位置
	 */
	private static final String ENCRYPT_CONFFILE = "D:\\app";

	/**
	 * 外部密钥
	 */
	private static final String PBKEY = "11111111111111112222222222222222";

	/**
	 * 过程密钥，初始向量-默认16个0
	 */
	private static final String CAL_INITVALUE = "0000000000000000";

	/**
	 * 默认终端机编号
	 */
	private static final String DEFALT_TERMINALNO = "1234567890FF";

	/**
	 * 充值模式
	 */
	private static final int TRAINSIN_MODE = 1;

	/**
	 * 充值的密钥索引号
	 */
	private static final int TRANSIN_KEYINDEX = 2;

	/**
	 * 分散次数
	 */
	private static final int UIDERIVE_TIMES = 1;


	/**
	 * 卡号，离散因子
	 */
	private String cardNo;

	/**
	 * 充值日期，充值终端需要与传入加密机的相同
	 * yyyyMMddHHmmss
	 */
	private String sdt;

	/**
	 * 充值金额
	 */
	private Integer incMoney;


	/**
	 * ED或EP余额（8）＋ED或EP联机交易序号 （4）＋密钥版本号（DLK）（2）＋算法标识（DLK）（2）＋伪随机数（IC卡）（8）＋MAC1（8）
	 */
	private String tacStr;


	private String obtainMac2() {
		SJFAPI sjfApi = SJFCrypto.getInstance();
		sjfApi.HsmSetCfgPath(ENCRYPT_CONFFILE);

		//构成：伪随机数 + ED或EP联机交易序号 + ''8000
		String randomData = buildRandomData();

		//构成：充值金额（8位，不足则前补0） + "02" + 终端编号 + yyyyMMdd
		String inData = buildInData();

		byte[] pbKey = txt2Hex(PBKEY);
		byte[] pbReferenceData = txt2Hex(cardNo);
		byte[] pbSessionRef = txt2Hex(randomData);
		byte[] pbIV = txt2Hex(CAL_INITVALUE);
		byte[] pbInData = txt2Hex(inData);

		try {
			byte[] out = sjfApi.SJF_GenMAC(TRAINSIN_MODE, GlobalData.SGD_3DES_MAC, GlobalData.MAC_PBOC, TRANSIN_KEYINDEX,
					GlobalData.KEY_TYPE_ZMK, pbKey, pbReferenceData, UIDERIVE_TIMES, pbSessionRef, pbIV, pbInData);
			return StringUtils.deleteWhitespace(PrintUtil.toHexString(out));
		} catch (Exception ex) {
			LOGGER.error(ex.getMessage(), ex);
		}
		return StringUtils.EMPTY;
	}

	private static byte[] txt2Hex(String text) {
		byte[] data = text.toLowerCase().getBytes();
		byte[] redata = new byte[(data.length / 2)];
		for (int i = 0; i < data.length; i += 2) {
			if (data[i] >= (byte) 97) {
				switch (data[i]) {
					case 97:
						data[i] = (byte) 58;
						break;
					case 98:
						data[i] = (byte) 59;
						break;
					case 99:
						data[i] = (byte) 60;
						break;
					case 100:
						data[i] = (byte) 61;
						break;
					case 101:
						data[i] = (byte) 62;
						break;
					case 102:
						data[i] = (byte) 63;
						break;
					default:
						break;
				}
			}
			if (data[i + 1] >= (byte) 97) {
				switch (data[i + 1]) {
					case 97:
						data[i + 1] = (byte) 58;
						break;
					case 98:
						data[i + 1] = (byte) 59;
						break;
					case 99:
						data[i + 1] = (byte) 60;
						break;
					case 100:
						data[i + 1] = (byte) 61;
						break;
					case 101:
						data[i + 1] = (byte) 62;
						break;
					case 102:
						data[i + 1] = (byte) 63;
						break;
					default:
						break;
				}
			}
			redata[i / 2] = (byte) (((data[i] - 48) * 16) + data[i + 1] - 48);
		}
		return redata;
	}

	private static String getAdditionalZero(String message) {
		String temp = CAL_INITVALUE;
		String result;
		if (message != null && !message.equals("")) {
			int length = message.length();
			if (length < 8) {
				result = temp.substring(0, 8 - length) + message;
			} else if (length == 8) {
				result = message;
			} else {
				return message.substring(0, 8);
			}
		} else {
			result = temp.substring(0, 8);
		}
		return result;
	}


	private String buildRandomData() {
		return tacStr.substring(16, 24) +
				tacStr.substring(8, 12) +
				"8000";
	}

	private String buildInData() {
		return getAdditionalZero(Integer.toHexString(incMoney)) +
				"02" +
				DEFALT_TERMINALNO +
				sdt;
	}


	public static void main(String[] args) {
		LOGGER.info("请按照提示输入相应的数据");
		LOGGER.info("1. 请输入充值卡号：");
		Scanner scanner = new Scanner(System.in);
		String cardNo = scanner.next();
		while (StringUtils.length(cardNo) != 16) {
			LOGGER.info("卡号输入错误，请重新输入");
			cardNo = scanner.next();
		}
		LOGGER.info("2. 请输入充值金额（单位为分）：");
		Integer incMoney = scanner.nextInt();
		LOGGER.info("3. 请输入充值时间：");
		String sdt = scanner.next();
		while (StringUtils.length(sdt) != 14) {
			LOGGER.info("卡号输入错误，请重新输入");
			sdt = scanner.next();
		}
		LOGGER.info("4. 请输入TAC字符串：");
		String tacStr = scanner.next();
		while (StringUtils.length(tacStr) != 32) {
			LOGGER.info("TAC输入错误，请重新输入：");
			tacStr = scanner.next();
		}
		EncryptService service = new EncryptService();
		service.cardNo = StringUtils.trim(cardNo);
		service.incMoney = incMoney;
		service.sdt = StringUtils.trim(sdt);
		service.tacStr = StringUtils.trim(tacStr);
		String mac2 = service.obtainMac2();
		if (StringUtils.isNotBlank(mac2)) {
			LOGGER.info("获取的MAC2为：{}", mac2);
		}
	}
}
